<?php

/**
 * This file is part of Webman AI.
 *
 * @author    walkor<walkor@workerman.net>
 * @copyright walkor<walkor@workerman.net>
 * @link      http://www.workerman.net/
 */

namespace plugin\ai\app\controller;

use Exception;
use plugin\ai\api\User;
use plugin\ai\app\event\data\EventData;
use plugin\ai\app\event\data\PaymentData;
use plugin\ai\app\model\AiOrder;
use plugin\ai\app\model\AiUser;
use plugin\ai\app\service\Plan;
use plugin\ai\app\service\User as UserService;
use Random\RandomException;
use RuntimeException;
use support\exception\BusinessException;
use support\Log;
use support\Request;
use support\Response;
use Webman\Event\Event;
use Yansongda\Pay\Exception\ContainerDependencyException;
use Yansongda\Pay\Exception\ContainerException;
use Yansongda\Pay\Exception\InvalidParamsException;
use Yansongda\Pay\Exception\ServiceNotFoundException;
use Yansongda\Pay\Pay;

class OrderController extends Base
{

    /**
     * 不需要登录的方法
     *
     * @var string[]
     */
    protected $noNeedLogin = ['status', 'getStatus', 'alipayNotify', 'wechatNotify'];

    /**
     * 创建订单
     * @param Request $request
     * @return Response
     * @throws Exception
     */
    public function create(Request $request): Response
    {
        $loginUid = session('user.id') ?? session('user.uid');
        if (!$loginUid) {
            return redirect('/app/user/login?redirect=' . urlencode('/app/ai/user/vip'));
        }
        $plan = (int)$request->post('plan');
        $paymentMethod = $request->post('paymentMethod');
        $order = $this->createOrder($loginUid, $plan, $paymentMethod);
        $data = ['orderId' => $order->order_id];
        return $this->json(0, 'ok', $data);
    }

    /**
     * @param $loginUid
     * @param $plan
     * @param $paymentMethod
     * @return AiOrder
     * @throws BusinessException|RandomException
     */
    protected function createOrder($loginUid, $plan, $paymentMethod): AiOrder
    {
        $plans = Plan::getSetting();
        if (!isset($plans[$plan])) {
            throw new BusinessException('参数错误');
        }
        $planData = $plans[$plan];
        $totalAmount = $planData['price'];
        // 创建订单
        $order = new AiOrder();
        $orderId = strtoupper($paymentMethod[0]) . date('YmdHis') . random_int(10000, 99999);
        $order->order_id = $orderId;
        $order->user_id = $loginUid;
        $order->total_amount = $totalAmount;
        $order->state = 'unpaid';
        $order->payment_method = $paymentMethod;
        $order->data = json_encode($planData);
        $order->save();

        return $order;
    }

    /**
     * @param Request $request
     * @return string|Response
     * @throws ContainerException
     */
    public function alipayH5(Request $request)
    {
        $loginUid = session('user.id') ?? session('user.uid');
        if (!$loginUid) {
            return response('<script>window.parent.location.reload();</script>');
        }

        $orderId = $request->input('orderId');
        $order = AiOrder::where('order_id', $orderId)->first();
        if (!$order) {
            return response('订单不存在');
        }
        $totalAmount = $order->total_amount;

        $scheme = $request->header('x-forwarded-proto');
        $host = $request->host();
        $hostWithOutPort = $request->host(true);
        $hostIsIp = filter_var($hostWithOutPort, FILTER_VALIDATE_IP);
        if (!$scheme) {
            if ($hostIsIp) {
                $scheme = 'http';
            } else {
                $scheme = strpos($request->header('referer', ''), 'https') === 0 ? 'https' : 'http';
            }
        }
        $payment_config = config('plugin.ai.payment');
        $payment_config['alipay']['default']['return_url'] = "$scheme://$host/app/ai/order/h5-pay?orderId=" . $orderId;
        $payment_config['alipay']['default']['notify_url'] = "$scheme://$host/app/ai/order/alipay-notify";

        Pay::config($payment_config);

        $subject = "充值官方AI助手会员";
        try {
            return Pay::alipay()->wap([
                'out_trade_no' => $orderId,
                'total_amount' => $totalAmount,
                'subject' => $subject,
                //'quit_url' => "$scheme://$host/app/ai",
            ])->getBody()->getContents();
        } catch (\Throwable $e) {
            throw new RuntimeException(isset($e->extra) ? json_encode($e->extra, JSON_UNESCAPED_UNICODE) : $e->getMessage());
        }
    }

    /**
     * 创建订单
     * @param Request $request
     * @return string
     * @throws Exception
     */
    public function alipayQr(Request $request)
    {
        $loginUid = session('user.id') ?? session('user.uid');
        if (!$loginUid) {
            return response('<script>window.parent.location.reload();</script>');
        }

        $orderId = $request->input('orderId');
        $order = AiOrder::where('order_id', $orderId)->first();
        if (!$order) {
            return response('订单不存在');
        }
        $totalAmount = $order->total_amount;

        $scheme = $request->header('x-forwarded-proto');
        $host = $request->host();
        $hostWithOutPort = $request->host(true);
        $hostIsIp = filter_var($hostWithOutPort, FILTER_VALIDATE_IP);
        if (!$scheme) {
            if ($hostIsIp) {
                $scheme = 'http';
            } else {
                $scheme = strpos($request->header('referer', ''), 'https') === 0 ? 'https' : 'http';
            }
        }
        $payment_config = config('plugin.ai.payment');
        $payment_config['alipay']['default']['return_url'] = ''; // 二维码模式留空，防止支付后跳转
        $payment_config['alipay']['default']['notify_url'] = "$scheme://$host/app/ai/order/alipay-notify";

        Pay::config($payment_config);

        $subject = "充值官方AI助手会员";
        try {
            return Pay::alipay()->web([
                'out_trade_no' => $orderId,
                'total_amount' => $totalAmount,
                'subject' => $subject,
                'qr_pay_mode' => 4,
                'qrcode_width' => 200,
            ])->getBody()->getContents();
        } catch (\Throwable $e) {
            throw new RuntimeException(isset($e->extra) ? json_encode($e->extra, JSON_UNESCAPED_UNICODE) : $e->getMessage());
        }
    }

    public function h5Pay(Request $request): Response
    {
        $orderId = $request->input('orderId');
        $order = AiOrder::where('order_id', $orderId)->first();
        if (!$order) {
            return response('订单不存在');
        }
        $planData = json_decode($order->data, true);
        $planName = $planData['name'] ?? "会员";
        $url = $order->state === 'paid' ? '' : ($order->payment_method === 'wechat' ? $this->wechatH5Url($order) : '/app/ai/order/alipay-h5?orderId=' . $orderId);
        return view('user/h5-pay', ['order' => $order, 'planName' => $planName, 'url' => $url]);
    }

    /**
     * 订单状态
     *
     * @param Request $request
     * @return Response
     */
    public function status(Request $request): Response
    {
        $orderId = $request->get('orderId');
        $order = AiOrder::where('order_id', $orderId)->first();
        if (!$order) {
            return $this->json(404, '订单不存在');
        }
        return $this->json(0, 'ok',  ['status' => $order->state]);
    }

    /**
     * 主动获取订单状态
     *
     * @param Request $request
     * @return Response
     * @throws BusinessException
     * @throws ContainerException
     * @throws InvalidParamsException
     * @throws ServiceNotFoundException
     */
    public function getStatus(Request $request): Response
    {
        $orderId = $request->get('orderId');
        $order = AiOrder::where('order_id', $orderId)->first();
        if (!$order) {
            return $this->json(404, '订单不存在');
        }
        // 微信需要transaction_id查询订单，而transaction_id是异步通知的
        if ($order->state === 'paid') {
            return $this->json(0, 'ok',  ['status' => $order->state]);
        }
        Pay::config(config('plugin.ai.payment'));
        if ($order->payment_method === 'wechat') {
            $search = [
                'out_trade_no' => $orderId,
            ];
            $wechatOrder = Pay::wechat()->find($search);
            if ($wechatOrder['trade_state'] !== 'SUCCESS') {
                return $this->json(0, 'ok',  ['status' => $order->state]);
            }
            $this->dealWechatOrder($wechatOrder);
        } else {
            $alipayOrder = Pay::alipay()->find($orderId);
            if ($alipayOrder['code'] != 10000 || !isset($alipayOrder['trade_status']) || ($alipayOrder['trade_status'] !== 'TRADE_SUCCESS' && $alipayOrder['trade_status'] !== 'TRADE_FINISHED')) {
                return $this->json(0, 'ok',  ['status' => $order->state]);
            }
            $this->dealAlipayOrder($alipayOrder);
        }

        $order = AiOrder::where('order_id', $orderId)->first();
        return $this->json(0, 'ok',  ['status' => $order->state]);
    }

    /**
     * 微信支付
     *
     * @param $order
     * @return string
     * @throws ContainerException
     */
    protected function wechatH5Url($order)
    {
        $orderId = $order->order_id;
        $request = request();
        $totalAmount = $order->total_amount;

        $scheme = $request->header('x-forwarded-proto');
        $host = $request->host();
        $hostWithOutPort = $request->host(true);
        $hostIsIp = filter_var($hostWithOutPort, FILTER_VALIDATE_IP);
        if (!$scheme) {
            if ($hostIsIp) {
                $scheme = 'http';
            } else {
                $scheme = strpos($request->header('referer', ''), 'https') === 0 ? 'https' : 'http';
            }
        }
        $payment_config = config('plugin.ai.payment');
        $payment_config['wechat']['default']['notify_url'] = "$scheme://$host/app/ai/order/wechat-notify";
        Pay::config($payment_config);

        $subject = "充值官方AI助手会员";
        $order = [
            'out_trade_no' => $orderId,
            'description' => $subject,
            'amount' => [
                'total' => (int)round($totalAmount * 100),
            ],
            'scene_info' => [
                'payer_client_ip' => $request->getRealIp(),
                'h5_info' => [
                    'type' => 'Wap',
                    'app_url' => "$scheme://$host/app/ai"
                ]
            ],
        ];
        try {
            $result = Pay::wechat()->wap($order);
        } catch (\Throwable $e) {
            throw new RuntimeException(isset($e->extra) ? json_encode($e->extra, JSON_UNESCAPED_UNICODE) : $e->getMessage());
        }
        if ($result['code']) {
            throw new RuntimeException($result['code'] . ':' . $result['message']);
        }
        return $result->h5_url . '&redirect_url=' . urlencode("$scheme://$host/app/ai/order/h5-pay?orderId=" . $orderId);
    }

    /**
     * 微信二维码
     *
     * @param Request $request
     * @return Response
     * @throws ContainerException
     */
    public function wechatQr(Request $request)
    {
        $loginUid = session('user.id') ?? session('user.uid');
        if (!$loginUid) {
            return response('<script>window.parent.location.reload();</script>');
        }

        $orderId = $request->input('orderId');
        $order = AiOrder::where('order_id', $orderId)->first();
        if (!$order) {
            return response('订单不存在');
        }
        $totalAmount = $order->total_amount;

        $scheme = $request->header('x-forwarded-proto');
        $host = $request->host();
        $hostWithOutPort = $request->host(true);
        $hostIsIp = filter_var($hostWithOutPort, FILTER_VALIDATE_IP);
        if (!$scheme) {
            if ($hostIsIp) {
                $scheme = 'http';
            } else {
                $scheme = strpos($request->header('referer', ''), 'https') === 0 ? 'https' : 'http';
            }
        }
        $payment_config = config('plugin.ai.payment');
        $payment_config['wechat']['default']['notify_url'] = "$scheme://$host/app/ai/order/wechat-notify";
        Pay::config($payment_config);

        $subject = "充值官方AI助手会员";
        $order = [
            'out_trade_no' => $orderId,
            'description' => $subject,
            'amount' => [
                'total' => (int)round($totalAmount * 100),
            ],
        ];
        try {
            $result = Pay::wechat()->scan($order);
        } catch (\Throwable $e) {
            throw new RuntimeException(isset($e->extra) ? json_encode($e->extra, JSON_UNESCAPED_UNICODE) : $e->getMessage());
        }
        if (isset($result['message'])) {
            return \response($result['message']);
        }
        return view('user/wechat', ['codeUrl' => $result['code_url']]);
    }

    /**
     * 支付宝支付通知
     *
     * @param Request $request
     * @return Response
     * @throws BusinessException
     * @throws ContainerException
     * @throws InvalidParamsException
     * @throws ServiceNotFoundException
     */
    public function alipayNotify(Request $request): Response
    {
        Pay::config(config('plugin.ai.payment'));
        $result = Pay::alipay()->callback($request->post());
        $alipayOrder = Pay::alipay()->find($result['out_trade_no']);
        $this->dealAlipayOrder($alipayOrder);
        return response('success');
    }

    /**
     * 微信支付通知
     *
     * @param Request $request
     * @return Response
     * @throws BusinessException
     * @throws ContainerException
     * @throws InvalidParamsException|ServiceNotFoundException
     */
    public function wechatNotify(Request $request): Response
    {
        Pay::config(config('plugin.ai.payment'));
        $wechatOrder = Pay::wechat()->callback($request->post());
        Log::info($wechatOrder);
        $wechatOrder = Pay::wechat()->find(['out_trade_no' => $wechatOrder['resource']['ciphertext']['out_trade_no']]);
        Log::info($wechatOrder);
        $this->dealWechatOrder($wechatOrder);
        return response(Pay::wechat()->success()->getBody());
    }

    /**
     * 处理订单
     *
     * @param $alipayOrder
     * @return void
     */
    protected function dealAlipayOrder($alipayOrder)
    {
        if ($alipayOrder['code'] != 10000 || !isset($alipayOrder['trade_status']) || ($alipayOrder['trade_status'] !== 'TRADE_SUCCESS' && $alipayOrder['trade_status'] !== 'TRADE_FINISHED')) {
            throw new RuntimeException("订单{$alipayOrder}状态错误 " . json_encode($alipayOrder, JSON_UNESCAPED_UNICODE));
        }
        $totalAmount = $alipayOrder['total_amount'];
        $orderId = $alipayOrder['out_trade_no'];
        $order = AiOrder::where('order_id', $orderId)->first();
        if (!$order) {
            throw new RuntimeException("订单{$orderId}不存在: $orderId");
        }
        $planData = json_decode($order->data, true);

        $date = date('Y-m-d H:i:s');
        if (!$order->paid_amount) {
            // 更新订单
            $order->state = 'paid';
            $order->paid_amount = $totalAmount;
            $order->paid_at = $result['send_pay_date'] ?? $date;
            $order->save();
            $this->addBalanceByPlanData($order->user_id, $planData);
        }

    }

    /**
     * @param $wechatOrder
     * @return void
     */
    protected function dealWechatOrder($wechatOrder)
    {
        $orderId = $wechatOrder['out_trade_no'];
        $order = AiOrder::where('order_id', $orderId)->first();
        if (!$order) {
            throw new RuntimeException("订单{$orderId}不存在: $orderId");
        }
        if ($wechatOrder['trade_state'] !== 'SUCCESS') {
            throw new RuntimeException("订单状态错误 订单状态{$wechatOrder['trade_state']}");
        }
        $totalAmount = $wechatOrder['amount']['total'];
        $planData = json_decode($order->data, true);
        $planData['transaction_id'] = $wechatOrder['transaction_id'];
        $order->data = $planData;
        $paidAt = date('Y-m-d H:i:s', strtotime($wechatOrder['success_time']));

        if (!$order->paid_amount) {
            // 更新订单
            $order->state = 'paid';
            $order->paid_amount = $totalAmount/100;
            $order->paid_at = $paidAt;
            $order->save();

            // 保存余额
            $this->addBalanceByPlanData($order->user_id, $planData);
        }

    }

    /**
     * 微信jsapi支付
     *
     * @param Request $request
     * @return Response
     * @throws ContainerException
     */
    public function jsapiPay(Request $request)
    {
        $orderId = $request->input('orderId');
        $order = AiOrder::where('order_id', $orderId)->first();
        if (!$order) {
            return response('订单不存在');
        }
        $sessionPrefix = 'plugin.ai';
        $request->session()->set($sessionPrefix . 'orderId', $orderId);
        $openid = $request->session()->get($sessionPrefix . 'openid');
        if (empty($openid)) {
            return $this->autoBind($request);
        }
        $planData = json_decode($order->data, true);
        $planName = $planData['name'] ?? "会员";

        $request = request();
        $totalAmount = $order->total_amount;

        $scheme = $request->header('x-forwarded-proto');
        $host = $request->host();
        $hostWithOutPort = $request->host(true);
        $hostIsIp = filter_var($hostWithOutPort, FILTER_VALIDATE_IP);
        if (!$scheme) {
            if ($hostIsIp) {
                $scheme = 'http';
            } else {
                $scheme = strpos($request->header('referer', ''), 'https') === 0 ? 'https' : 'http';
            }
        }
        $payment_config = config('plugin.ai.payment');
        Pay::config($payment_config);
        $subject = "AI助手会员充值";
        $mpOrder = [
            'out_trade_no' => $orderId,
            'description' => $subject,
            'notify_url' => "$scheme://$host/app/ai/order/wechat-notify",
            'time_expire' => date('c', strtotime('+9 minutes')),
            'amount' => [
                'total' => (int)round($totalAmount * 100),
            ],
            'payer' => [
                'openid' => $openid,
            ]
        ];
        try {
            $result = Pay::wechat()->mp($mpOrder);
        } catch (\Throwable $e) {
            throw new RuntimeException(isset($e->extra) ? json_encode($e->extra, JSON_UNESCAPED_UNICODE) : $e->getMessage());
        }
        if ($result['code']) {
            throw new RuntimeException($result['code'] . ':' . $result['message']);
        }
        return view('user/wechat-mp-pay', ['order' => $order, 'planName' => $planName, 'payData' => $result]);
    }

    /**
     * 静默oauth登陆
     *
     * @param Request $request
     * @return Response
     */
    private function autoBind(Request $request)
    {
        $isWechat = $this->isWechat($request->header('user-agent'));
        if (!$isWechat) {
            return redirect('/');
        }
        $host = $request->host();
        $scheme = $request->header('x-forwarded-proto');
        $hostWithOutPort = $request->host(true);
        $hostIsIp = filter_var($hostWithOutPort, FILTER_VALIDATE_IP);
        if (!$scheme) {
            if ($hostIsIp) {
                $scheme = 'http';
            } else {
                $scheme = strpos($request->header('referer', ''), 'https') === 0 ? 'https' : 'http';
            }
        }
        return $this->directToWechat($host, $scheme);
    }

    /**
     * 检测微信环境
     *
     * @param $userAgent
     * @return bool
     */
    private function isWechat($userAgent): bool
    {
        if (!strpos($userAgent, 'MicroMessenger')) {
            return false;
        } else {
            return true;
        }

    }

    /**
     * oauth 执行跳转
     *
     * @param $host
     * @param $scheme
     * @return Response
     */
    private function directToWechat($host, $scheme)
    {
        $config = config('plugin.ai.payment.wechat.default');
        $redirectUrl = urlencode("$scheme://$host/app/ai/order/callback");
        $url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' . $config['mp_app_id'] . '&redirect_uri=' . $redirectUrl . '&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect';
        return redirect($url);
    }

    /**
     * oauth 回调接收并重定向到订单
     *
     * @param Request $request
     * @return Response
     * @throws BusinessException
     */
    public function callback(Request $request)
    {
        $sessionPrefix = 'plugin.ai';
        $code = $request->get('code');
        $config = config('plugin.ai.payment.wechat.default');
        $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $config['mp_app_id'] . '&secret=' . $config['mp_app_secret'] . '&code=' . $code . '&grant_type=authorization_code';
        $buffer = file_get_contents($url);
        if (!$buffer) {
            throw new BusinessException('access_token获取失败');
        }
        $buffer = json_decode($buffer, true);
        $request->session()->set($sessionPrefix . 'openid', $buffer['openid']);
        $pendingOrderId = $request->session()->get($sessionPrefix . 'orderId');
        if (empty($pendingOrderId)) {
            return redirect('/');
        } else {
            return redirect('/app/ai/order/jsapi-pay?orderId=' . $pendingOrderId);
        }
    }

    /**
     * @param $userId
     * @param $planData
     * @return void
     */
    protected function addBalanceByPlanData($userId, $planData)
    {
        User::addBalanceByPlanData($userId, $planData);
        $eventData = new PaymentData($userId, $planData);
        Event::dispatch('ai.payment.success', $eventData);
    }

}
